import os
import uuid
from pathlib import Path
from fastapi import UploadFile, HTTPException


class FileUploader:
    """
    文件上传通用类，处理文件存储、类型验证和大小限制
    """
    def __init__(
        self, 
        upload_dir: str = "uploads", 
        allowed_extensions: list = None, 
        max_size: int = 5 * 1024 * 1024  # 默认5MB
    ):
        """
        初始化文件上传器
        :param upload_dir: 上传文件存储目录
        :param allowed_extensions: 允许上传的文件扩展名列表，如['.jpg', '.png']
        :param max_size: 最大文件大小(字节)
        """
        self.upload_dir = Path(upload_dir)
        self.allowed_extensions = allowed_extensions or []
        self.max_size = max_size

        # 创建上传目录（如果不存在）
        self.upload_dir.mkdir(parents=True, exist_ok=True)

    def _validate_file_type(self, filename: str) -> bool:
        """
        验证文件类型是否允许上传
        :param filename: 文件名
        :return: 允许上传返回True，否则False
        """
        if not self.allowed_extensions:
            return True  # 如果未指定允许的扩展名，则允许所有类型
        ext = os.path.splitext(filename)[1].lower()
        return ext in self.allowed_extensions

    def _validate_file_size(self, file_size: int) -> bool:
        """
        验证文件大小是否超过限制
        :param file_size: 文件大小(字节)
        :return: 未超过限制返回True，否则False
        """
        return file_size <= self.max_size

    async def save_file(self, file: UploadFile) -> str:
        """
        保存上传的文件并返回文件路径
        :param file: FastAPI上传文件对象
        :return: 保存后的文件相对路径
        """
        # 验证文件大小
        file_size = await file.read()
        if not self._validate_file_size(len(file_size)):
            raise HTTPException(
                status_code=400, 
                detail=f"文件大小超过限制，最大允许{self.max_size / (1024*1024)}MB"
            )
        await file.seek(0)  # 重置文件指针

        # 验证文件类型
        if not self._validate_file_type(file.filename):
            raise HTTPException(
                status_code=400, 
                detail=f"不支持的文件类型，允许的类型: {','.join(self.allowed_extensions)}"
            )

        # 生成唯一文件名
        filename = f"{uuid.uuid4().hex}{os.path.splitext(file.filename)[1].lower()}"
        file_path = self.upload_dir / filename

        # 保存文件
        with open(file_path, "wb") as f:
            f.write(await file.read())

        return str(file_path.relative_to(Path.cwd()))